package com.xinruke.hostels.reservation.service;

import com.xinruke.hostels.cache.extract.extractor.HotelInfoCacheExtractor;
import com.xinruke.hostels.cache.extract.extractor.ReservationCacheExtractor;
import com.xinruke.hostels.common.exception.ArgumentException;
import com.xinruke.hostels.common.po.MhHuiyuanQinshu;
import com.xinruke.hostels.common.po.MhNianyouhuicishu;
import com.xinruke.hostels.common.po.MhNianyouhuicishuxiaofeijilu;
import com.xinruke.hostels.common.po.MhYouhuicishuguize;
import com.xinruke.hostels.common.po.constants.YwYudingDingdanZixiangConstants;
import com.xinruke.hostels.common.util.CustomSecurity;
import com.xinruke.hostels.common.util.DateCalendarUtil;
import com.xinruke.hostels.common.vo.ResultVO;
import com.xinruke.hostels.reservation.interfaces.dto.*;
import com.xinruke.hostels.reservation.mapper.MhNianyouhuicishuMapper;
import com.xinruke.hostels.reservation.mapper.MhNianyouhuicishuxiaofeijiluMapper;
import com.xinruke.hostels.reservation.mapper.MhYouhuicishuguizeMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.util.Sqls;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author GuoZheng
 * @ClassName DiscountsService
 * @date：2019/7/3
 * @version: V1.0.0
 * @description：
 */
@Service
@Slf4j
public class DiscountsService {
    @Resource
    private MhNianyouhuicishuMapper mhNianyouhuicishuMapper;

    @Resource
    private MhNianyouhuicishuxiaofeijiluMapper mhNianyouhuicishuxiaofeijiluMapper;

    //所有房型优惠价
    @Value("${hotel.discounts.jiage}")
    private BigDecimal youhuijiage;

    @Resource
    private HotelInfoCacheExtractor hotelInfoCacheExtractor;
    @Resource
    private ReservationCacheExtractor reservationCacheExtractor;
    @Resource
    private MhYouhuicishuguizeMapper mhYouhuicishuguizeMapper;

    /**
     * 查询剩余优惠次数，不考虑入住性质问题
     *
     * @param params
     * @return
     */
    public ResultVO<Integer> queryRemainingDiscount(DiscountsRemainingQueryDTO params) {
        ResultVO<Integer> resultVO = ResultVO.success();
        //获取联系人信息
        //MhHuiyuanQinshu contactsDetail = reservationCache.getContactsDetail(params.getContactIds());

        MhHuiyuanQinshu huiyuanQinshu = reservationCacheExtractor.getMhHuiyuanQinshu(params.getContactIds());
        //如果不是直系亲属，直接返回0(暂时不会进入这个方法)
        /*if (contactsDetail.getGuanxi() > MhHuiyuanQinshu.GUANXI_PEIOUFUMU) {
            resultVO.setData(0);
            return resultVO;
        }*/

        //获取入住时间年份,并查询这个年份是否存在
        Integer year = DateCalendarUtil.getYear(params.getRuzhushijian());
        MhNianyouhuicishu mhNianyouhuicishus = mhNianyouhuicishuMapper.selectOneByExample(Example.builder(MhNianyouhuicishu.class)
                .where(Sqls.custom()
                        .andEqualTo("shanchubiaoshi", MhNianyouhuicishu.SHANCHUBIAOSHI_BUSHANCHU)
                        .andEqualTo("zhengjianleixing", huiyuanQinshu.getZhengjianleixing())
                        .andEqualTo("zhengjianhao", huiyuanQinshu.getShenfenzhenghao())
                        .andEqualTo("nian", year)
                ).build());

        if (mhNianyouhuicishus == null) {
            //添加该用户在指定年限的次数
            DiscountsAddQueryDTO addQueryDTO = new DiscountsAddQueryDTO();
            addQueryDTO.setZhengjianleixing(huiyuanQinshu.getZhengjianleixing());
            addQueryDTO.setZhengjianhao(CustomSecurity.decodeIDCard(huiyuanQinshu.getShenfenzhenghao()));
            addQueryDTO.setQinshuId(huiyuanQinshu.getId());
            addQueryDTO.setHuiyuanId(huiyuanQinshu.getHuiyuanId());
            addQueryDTO.setQinshuguanxi(huiyuanQinshu.getGuanxi());
            Integer chushicishu = insert(addQueryDTO, year);

            //新插入的优惠则直接返回初始次数
            resultVO.setData(chushicishu);
            return resultVO;
        }

        //剩余优惠次数减去预订中次数得到剩余预订次数
        int remainCount = mhNianyouhuicishus.getShengyucishu() - mhNianyouhuicishus.getYudingzhongcishu();
        resultVO.setData(remainCount);
        return resultVO;
    }

    public ResultVO<Integer> queryRemainingDiscountByIdentity(DiscountsRemainingByIdentityQueryDTO params) {
        ResultVO<Integer> resultVO = ResultVO.success();
        Integer year = DateCalendarUtil.getYear(params.getRuzhushijian());
        MhNianyouhuicishu mhNianyouhuicishus = mhNianyouhuicishuMapper.selectOneByExample(Example.builder(MhNianyouhuicishu.class)
                .where(Sqls.custom()
                        .andEqualTo("shanchubiaoshi", MhNianyouhuicishu.SHANCHUBIAOSHI_BUSHANCHU)
                        .andEqualTo("zhengjianleixing", params.getZhengjianleixing())
                        .andEqualTo("zhengjianhao", CustomSecurity.encodeIDCard(params.getZhengjianhao()))
                        .andEqualTo("nian", year)
                ).build());

        if (mhNianyouhuicishus == null) {
            //直接返回0
            resultVO.setData(0);
            return resultVO;
        }

        //剩余优惠次数减去预订中次数得到剩余预订次数
        int remainCount = mhNianyouhuicishus.getShengyucishu() - mhNianyouhuicishus.getYudingzhongcishu();
        resultVO.setData(remainCount);
        return resultVO;
    }

    /**
     * 查询优惠之后的价格
     *
     * @param params
     * @return
     */
    public ResultVO<DiscountsIfCanQueryResultDTO> queryDiscountsPrice(DiscountsIfCanQueryDTO params) {
        ResultVO<DiscountsIfCanQueryResultDTO> resultVO = ResultVO.success();
        //预订天数
        BigDecimal dayDifferent = BigDecimal.valueOf(DateCalendarUtil.getDayDifferent(params.getRuzhushijian(), params.getJieshushijian()));
        //预订数量
        BigDecimal amount = BigDecimal.valueOf(params.getShuliang());
        //总数量=预订天数*预订数量
        BigDecimal totalAmount = dayDifferent.multiply(amount);

        //得到需要优惠的次数
        ResultVO<BigDecimal> discountAmount = getDiscountAmount(params);
        BigDecimal needDiscountAmount = discountAmount.getData();

        DiscountsIfCanQueryResultDTO discountsIfCanQueryResultDTO = new DiscountsIfCanQueryResultDTO();
        //如果优惠次数不足，返回门市价*天数*房间数
        if (needDiscountAmount.compareTo(new BigDecimal(0)) == 0) {
            discountsIfCanQueryResultDTO.setIsDiscounts(false);
            discountsIfCanQueryResultDTO.setDiscountsPrice(params.getMendianjia().multiply(totalAmount));

        } else {
            discountsIfCanQueryResultDTO.setIsDiscounts(true);
            discountsIfCanQueryResultDTO.setDiscountsPrice(youhuijiage.multiply(totalAmount));
            //如果优惠次数足够,则优惠金额为单间优惠价格*天数*房间数
        }
        resultVO.setData(discountsIfCanQueryResultDTO);
        return resultVO;
    }

    /**
     * 返回本次需要优惠的次数
     *
     * @param params
     * @return
     */
    public ResultVO<BigDecimal> getDiscountAmount(DiscountsIfCanQueryDTO params) {
        ResultVO<BigDecimal> resultVO = ResultVO.success();
        //从缓存中拿到房型价格
        params.setMendianjia(hotelInfoCacheExtractor.getRoomTypePriceById(params.getFangxingId()));

        //预订天数
        BigDecimal dayDifferent = BigDecimal.valueOf(DateCalendarUtil.getDayDifferent(params.getRuzhushijian(), params.getJieshushijian()));
        //预订数量
        BigDecimal amount = BigDecimal.valueOf(params.getShuliang());
        //总数量=预订天数*预订数量
        BigDecimal totalAmount = dayDifferent.multiply(amount);

        //入住性质为公 直接返回优惠价乘以总次数
        if (params.getRuzhuxingzhi() == YwYudingDingdanZixiangConstants.RUZHUXINGZHI_YINGONG) {
            resultVO.setData(youhuijiage.multiply(totalAmount));
            return resultVO;
        }

        DiscountsRemainingQueryDTO queryDTO = new DiscountsRemainingQueryDTO();
        BeanUtils.copyProperties(params, queryDTO);
        //调用方法查询人员剩余优惠次数,如果联系人不是直系亲属，返回的优惠次数是0
        ResultVO<Integer> remaining = queryRemainingDiscount(queryDTO);
        if (remaining.getCode() == ResultVO.FAIL) {
            throw new ArgumentException("查询人员剩余优惠次数失败");
        }

        //剩余优惠次数
        BigDecimal discountCounts = BigDecimal.valueOf(remaining.getData());
        //如果剩余优惠次数足够优惠本次则返回本次需要优惠的次数
        if (discountCounts.compareTo(totalAmount) > 0) {
            resultVO.setData(totalAmount);
        } else {
            log.info("剩余优惠次数不足以优惠指定次数，剩余优惠次数：" + discountCounts + "，需要的优惠次数：" + totalAmount);
            resultVO.setData(new BigDecimal(0));
        }

        return resultVO;
    }


    /**
     * 添加优惠
     *
     * @param params
     * @return
     */
    @Transactional
    public ResultVO add(DiscountsAddQueryDTO params) {
        //1. 查询该用户是否存在当前年份的优惠

        //获取当前时间年份
        Integer year = DateCalendarUtil.getYear(new Date());
        //查询当前时间是否有该用户
        MhNianyouhuicishu mhNianyouhuicishus = mhNianyouhuicishuMapper.selectOneByExample(Example.builder(MhNianyouhuicishu.class)
                .where(Sqls.custom()
                        .andEqualTo("shanchubiaoshi", MhNianyouhuicishu.SHANCHUBIAOSHI_BUSHANCHU)
                        .andEqualTo("zhengjianleixing", params.getZhengjianleixing())
                        .andEqualTo("zhengjianhao", CustomSecurity.encodeIDCard(params.getZhengjianhao()))
                        .andEqualTo("nian", year)
                )
                .build());

        //1.1如果不存当前年份的优惠，进行添加
        if (mhNianyouhuicishus == null) {

            //MhHuiyuanQinshu contactsDetail = reservationUserApi.contactsDetail(params.getQinshuId()).getData();
            //插入数据
            insert(params, year);
        }

        return ResultVO.success();
    }

    /**
     * 插入优惠表数据
     *
     * @param params
     * @param year
     */
    @Transactional
    public Integer insert(DiscountsAddQueryDTO params, Integer year) {
        params.setZhengjianhao(CustomSecurity.encodeIDCard(params.getZhengjianhao()));

        MhNianyouhuicishu mhNianyouhuicishu = new MhNianyouhuicishu();
        BeanUtils.copyProperties(params, mhNianyouhuicishu);

        mhNianyouhuicishu.setZhengjianhao(params.getZhengjianhao());
        mhNianyouhuicishu.setChuangjianshijian(new Date());
        mhNianyouhuicishu.setGengxinshijian(new Date());
        mhNianyouhuicishu.setShanchubiaoshi(MhNianyouhuicishu.SHANCHUBIAOSHI_BUSHANCHU);

        List<MhYouhuicishuguize> youhuicishuguizes = mhYouhuicishuguizeMapper.selectByExample(Example.builder(MhYouhuicishuguize.class)
                .andWhere(Sqls.custom()
                        .andEqualTo("qinshuguanxi", params.getQinshuguanxi())
                        .andEqualTo("shanchubiaoshi", 0))
                .build());
        if (null == youhuicishuguizes || youhuicishuguizes.size() != 1) {
            ResultVO.fail("关系优惠次数查询失败");
        }

        mhNianyouhuicishu.setCishu(youhuicishuguizes.get(0).getCishu());
        mhNianyouhuicishu.setShengyucishu(youhuicishuguizes.get(0).getCishu());
        mhNianyouhuicishu.setNian(year);
        mhNianyouhuicishu.setYudingzhongcishu(MhNianyouhuicishu.YUDINGZHONGCISHU);
        mhNianyouhuicishu.setHuiyuanId(params.getHuiyuanId());

        int insert = mhNianyouhuicishuMapper.insert(mhNianyouhuicishu);

        if (insert == 0) {
            throw new ArgumentException("插入优惠失败");
        }
        return youhuicishuguizes.get(0).getCishu();
    }

    /**
     * 预订不用事务，会根据上级回退
     *
     * @param params
     * @return
     */
    public ResultVO reservation(List<DiscountsReservationQueryDTO> params) {
        //如果不需要更新优惠则跳过
        if (CollectionUtils.isEmpty(params)) {
            return ResultVO.success();
        }
        //得到天数差
        params.forEach(p -> {
            Integer dayDifferent = DateCalendarUtil.getDayDifferent(p.getRuzhushijian(), p.getJieshushijian());
            p.setDayDifferent(dayDifferent);
        });

        //更新预订中次数
        mhNianyouhuicishuMapper.updateYudingzhongcishu(params);

        return ResultVO.success();
    }

    /**
     * 入住时更新优惠次数和预订中次数
     * 1.预订中次数减去天数差
     * 2.优惠次数减去天数差
     *
     * @param params
     * @return
     */
    @Transactional
    public ResultVO checkIn(@Valid List<DiscountsCheckInQueryDTO> params) {
        //如果不需要更新优惠则跳过
        if (CollectionUtils.isEmpty(params)) {
            return ResultVO.success();
        }
        //得到天数差
        params.forEach(p -> {
            Integer dayDifferent = DateCalendarUtil.getDayDifferent(p.getRuzhushijian(), p.getJieshushijian());
            p.setDayDifferent(dayDifferent);
        });
        //更新预订中次数和优惠次数
        int i = mhNianyouhuicishuMapper.checkIn(params);
        if (i < 1) {
            throw new ArgumentException("更新优惠次数失败");
        }

        //添加优惠消费记录
        MhNianyouhuicishuxiaofeijilu jilu = new MhNianyouhuicishuxiaofeijilu();
        BeanUtils.copyProperties(params, jilu);
        int insert = mhNianyouhuicishuxiaofeijiluMapper.insert(jilu);
        if (insert < 1) {
            throw new ArgumentException("插入日志失败");
        }
        return ResultVO.success();
    }

    /**
     * 提前退房补偿
     * 1.计算实际退房时间与预订退房时间差值
     * 2.优惠次数增加 天数差
     *
     * @param params
     * @return
     */
    @Transactional
    public ResultVO makeUp(@Valid List<DiscountsMakeUpQueryDTO> params) {
        //如果不需要更新优惠则跳过
        if (CollectionUtils.isEmpty(params)) {
            return ResultVO.success();
        }
        //得到天数差
        params.forEach(p -> {
            Integer dayDifferent = DateCalendarUtil.getDayDifferent(p.getShijijieshushijian(), p.getJieshushijian());
            p.setDayDifferent(dayDifferent);
        });
        //更新预订中次数和优惠次数
        mhNianyouhuicishuMapper.makeUp(params);

        return ResultVO.success();
    }

    public ResultVO cancelReservation(List<DiscountsReservationQueryDTO> params) {
        //如果不需要更新优惠则跳过
        if (CollectionUtils.isEmpty(params)) {
            return ResultVO.success();
        }
        //得到天数差
        params.forEach(p -> {
            Integer dayDifferent = DateCalendarUtil.getDayDifferent(p.getRuzhushijian(), p.getJieshushijian());
            p.setDayDifferent(dayDifferent);
        });

        //更新预订中次数
        mhNianyouhuicishuMapper.cancelReservation(params);

        return ResultVO.success();
    }


}
